001 /*
002 * Copyright 2006 Stephen J. McConnell.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
013 * implied.
014 *
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018
019 package net.dpml.state;
020
021 import java.io.IOException;
022 import java.io.OutputStream;
023 import java.io.Writer;
024 import java.io.OutputStreamWriter;
025
026 import javax.xml.XMLConstants;
027
028 import net.dpml.state.Trigger.TriggerEvent;
029
030 /**
031 * Construct a state graph.
032 */
033 public class StateEncoder
034 {
035 private static final String XML_HEADER =
036 "<?xml version=\"1.0\"?>";
037
038 private static final String STATE_SCHEMA_URN = "link:xsd:dpml/lang/dpml-state#1.0";
039
040 /**
041 * Externalize the part to XML.
042 * @param state the state graph to externalize
043 * @param output the output stream
044 * @exception IOException if an IO error occurs
045 */
046 public void export( State state, OutputStream output ) throws IOException
047 {
048 final Writer writer = new OutputStreamWriter( output );
049
050 writer.write( XML_HEADER );
051 writer.write( "\n\n" );
052 writer.write( "<state xmlns=\""
053 + STATE_SCHEMA_URN
054 + "\""
055 + "\n xmlns:xsi=\""
056 + XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI
057 + "\"" );
058 if( state.isTerminal() )
059 {
060 writer.write( " terminal=\"true\">" );
061 }
062 else
063 {
064 writer.write( ">" );
065 }
066 writer.write( "\n" );
067 writeBody( writer, state, "" );
068 writer.write( "\n" );
069 writer.write( "\n</state>" );
070 writer.write( "\n" );
071 writer.flush();
072 output.close();
073 }
074
075 /**
076 * Write the state.
077 * @param writer the stream writer
078 * @param state the state to externalize
079 * @param pad the pad offset
080 * @exception IOException if an IO error occurs
081 */
082 public void writeState( Writer writer, State state, String pad ) throws IOException
083 {
084 if( isEmpty( state ) )
085 {
086 return;
087 }
088 else
089 {
090 writer.write( "\n" + pad + "<state xmlns=\"" + STATE_SCHEMA_URN + "\"" );
091 if( state.isTerminal() )
092 {
093 writer.write( " terminal=\"true\"" );
094 }
095 writer.write( ">" );
096 writeBody( writer, state, pad + " " );
097 writer.write( "\n" + pad + "</state>" );
098 }
099 }
100
101 private void writeBody( Writer writer, State state, String pad ) throws IOException
102 {
103 Trigger[] triggers = state.getTriggers();
104 Transition[] transitions = state.getTransitions();
105 Operation[] operations = state.getOperations();
106 Interface[] interfaces = state.getInterfaces();
107 State[] states = state.getStates();
108
109 writeTriggers( writer, triggers, pad );
110 writeTransitions( writer, transitions, pad );
111 writeOperations( writer, operations, pad );
112 writeInterfaces( writer, interfaces, pad );
113 writeStates( writer, states, pad );
114 }
115
116 private void writeTriggers( Writer writer, Trigger[] triggers, String pad ) throws IOException
117 {
118 if( triggers.length == 0 )
119 {
120 return;
121 }
122 else
123 {
124 for( int i=0; i<triggers.length; i++ )
125 {
126 Trigger trigger = triggers[i];
127 writeTrigger( writer, trigger, pad );
128 }
129 }
130 }
131
132 private void writeTrigger( Writer writer, Trigger trigger, String pad ) throws IOException
133 {
134 TriggerEvent event = trigger.getEvent();
135 writer.write( "\n" + pad + "<trigger event=\"" );
136 writer.write( event.getName() );
137 writer.write( "\">" );
138 Action action = trigger.getAction();
139 writeAction( writer, action, pad + " " );
140 writer.write( "\n" + pad + "</trigger>" );
141 }
142
143 private void writeTransitions( Writer writer, Transition[] transitions, String pad ) throws IOException
144 {
145 if( transitions.length == 0 )
146 {
147 return;
148 }
149 else
150 {
151 for( int i=0; i<transitions.length; i++ )
152 {
153 Transition transition = transitions[i];
154 writeTransition( writer, transition, pad );
155 }
156 }
157 }
158
159 private void writeOperations( Writer writer, Operation[] operations, String pad ) throws IOException
160 {
161 if( operations.length == 0 )
162 {
163 return;
164 }
165 else
166 {
167 for( int i=0; i<operations.length; i++ )
168 {
169 Operation operation = operations[i];
170 writeOperation( writer, operation, pad );
171 }
172 }
173 }
174
175 private void writeInterfaces( Writer writer, Interface[] interfaces, String pad ) throws IOException
176 {
177 if( interfaces.length == 0 )
178 {
179 return;
180 }
181 else
182 {
183 for( int i=0; i<interfaces.length; i++ )
184 {
185 Interface spec = interfaces[i];
186 writeInterface( writer, spec, pad );
187 }
188 }
189 }
190
191 private void writeStates( Writer writer, State[] states, String pad ) throws IOException
192 {
193 if( states.length == 0 )
194 {
195 return;
196 }
197 else
198 {
199 for( int i=0; i<states.length; i++ )
200 {
201 State state = states[i];
202 writeNestedState( writer, state, pad );
203 }
204 }
205 }
206
207 private void writeTransition( Writer writer, Transition transition, String pad ) throws IOException
208 {
209 String name = transition.getName();
210 String target = transition.getTargetName();
211 writer.write( "\n" + pad + "<transition name=\"" + name + "\" target=\"" + target + "\"" );
212 Operation operation = transition.getOperation();
213 if( null == operation )
214 {
215 writer.write( "/>" );
216 }
217 else
218 {
219 writer.write( ">" );
220 writeOperation( writer, operation, pad + " " );
221 writer.write( "\n" + pad + "</transition>" );
222 }
223 }
224
225 private void writeOperation( Writer writer, Operation operation, String pad ) throws IOException
226 {
227 String name = operation.getName();
228 String method = operation.getMethodName();
229 writer.write( "\n" + pad + "<operation name=\"" + name + "\"" );
230 if( null != method )
231 {
232 writer.write( " method=\"" + method + "\"" );
233 }
234 writer.write( "/>" );
235 }
236
237 private void writeInterface( Writer writer, Interface spec, String pad ) throws IOException
238 {
239 String classname = spec.getClassname();
240 writer.write( "\n" + pad + "<interface class=\"" + classname + "\"/>" );
241 }
242
243 private void writeAction( Writer writer, Action action, String pad ) throws IOException
244 {
245 if( action instanceof Transition )
246 {
247 Transition transition = (Transition) action;
248 writeTransition( writer, transition, pad );
249 }
250 else if( action instanceof Operation )
251 {
252 Operation operation = (Operation) action;
253 writeOperation( writer, operation, pad );
254 }
255 else if( action instanceof Interface )
256 {
257 Interface spec = (Interface) action;
258 writeInterface( writer, spec, pad );
259 }
260 else if( action instanceof ExecAction )
261 {
262 ExecAction exec = (ExecAction) action;
263 writeExecAction( writer, exec, pad );
264 }
265 else if( action instanceof ApplyAction )
266 {
267 ApplyAction apply = (ApplyAction) action;
268 writeApplyAction( writer, apply, pad );
269 }
270 else
271 {
272 final String error =
273 "Unrecognized action class ["
274 + action.getClass().getName()
275 + "].";
276 throw new IOException( error );
277 }
278 }
279 private void writeExecAction( Writer writer, ExecAction action, String pad ) throws IOException
280 {
281 String id = action.getID();
282 writer.write( "\n" + pad + "<exec id=\"" + id + "\"/>" );
283 }
284
285 private void writeApplyAction( Writer writer, ApplyAction action, String pad ) throws IOException
286 {
287 String id = action.getID();
288 writer.write( "\n" + pad + "<apply id=\"" + id + "\"/>" );
289 }
290
291 private void writeNestedState( Writer writer, State state, String pad ) throws IOException
292 {
293 String name = state.getName();
294 writer.write( "\n" + pad + "<state name=\"" + name + "\"" );
295 if( state.isTerminal() )
296 {
297 writer.write( " terminal=\"true\">" );
298 }
299 else
300 {
301 writer.write( ">" );
302 }
303 writeBody( writer, state, pad + " " );
304 writer.write( "\n" + pad + "</state>" );
305 }
306
307 private boolean isEmpty( State state )
308 {
309 if( state.getTriggers().length > 0 )
310 {
311 return false;
312 }
313 if( state.getTransitions().length > 0 )
314 {
315 return false;
316 }
317 if( state.getOperations().length > 0 )
318 {
319 return false;
320 }
321 if( state.getInterfaces().length > 0 )
322 {
323 return false;
324 }
325 if( state.getStates().length > 0 )
326 {
327 return false;
328 }
329 return true;
330 }
331 }